#include "Munjiza_nbs_par.h"


void Munjiza_nbs_par::alocaElementosNaLinha ()
{
    linhas= new elementosLinha[Rows];

}
void Munjiza_nbs_par::alocaElementosNasColunas ()
{
    ColLinhaAtual= new elementosColuna*[omp_get_num_procs()];
    ColLinhaPosterior= new elementosColuna*[omp_get_num_procs()];
    for (int i=0; i!=omp_get_num_procs(); i++)
    {
        ColLinhaAtual[i] = new elementosColuna[Cols];
        ColLinhaPosterior[i] = new elementosColuna[Cols];
    }
}
void Munjiza_nbs_par::iniciaMatriz( REAL max_rad, REAL min_r[2], REAL max_r[2] )
{

        MinR[0] = min_r[0];
        MinR[1] = min_r[1];
        MaxR[0] = max_r[0];
        MaxR[1] = max_r[1];

        tamCelula = max_rad*2.0;
        Rows = (int32_t) ((MaxR[1] - MinR[1]) / tamCelula);
        Cols = (int32_t) ((MaxR[0] - MinR[0]) / tamCelula);

        int qtdEsferas=0;
        for (SPHERE* s = Spheres->Begin; s != NULL; s = s->Next)
        {
            qtdEsferas++;
        }
        printf ("\nMunjiza-NBS -> Vetor-Coluna:%d Vetor-Linha:%d\n",Cols,Rows);
        ImprimeMem("Munjiza-NBS", (((((2*Cols)*sizeof(elementosColuna)*omp_get_num_procs())+Rows*sizeof(elementosLinha)) + 2*qtdEsferas*sizeof(SPHERE*))/1024));
        alocaElementosNaLinha();
        alocaElementosNasColunas();
}

Munjiza_nbs_par::Munjiza_nbs_par(REAL max_rad, MODEL* pModel,ForcaInteracao* t)
{
    Spheres = &pModel->SPHList;
    iniciaMatriz( max_rad,pModel->BoxMin, pModel->BoxMax );
    Forca = t;
    md=pModel;

    linhaProc=new vector<SPHERE*>*[pModel->numProceEsf];
    for (int i=0; i!=pModel->numProceEsf; i++)
    {
        linhaProc[i]=new vector<SPHERE*>[Rows];
    }
}
void Munjiza_nbs_par::ZeraPonteiros()
{
    #pragma omp for schedule (dynamic,1)
    for (int i = 0; i < md->numProceEsf; i++)
    {
        SPHERE* testeFim;
        if (i!=md->numProceEsf-1) testeFim = md->threads[i+1];
        else testeFim=NULL;
        for (SPHERE* s = md->threads[i]; s != testeFim; s = s->Next)
        {
            s->CellNext=NULL;
        }
    }

}
void Munjiza_nbs_par::CriaVetorLinha()
{
    #pragma omp single
    {
        //Etapa 1: Popular o vetor-linha
        for (SPHERE* s = Spheres->Begin; s != NULL; s = s->Next)
        {
            int32_t row = Max( Min( (int32_t)( (s->R[1] - MinR[1]) / tamCelula ), Rows-1 ), 0 );
            linhas[row].novo=true;
            linhas[row].esferas.push_back(s);
        }
    }
    //Barreira implícita
}
void Munjiza_nbs_par::CriaVetorLinhaParalelo()
{

    //Etapa 1: Popular o vetor-linha
    #pragma omp for schedule (static,1)
    for (int i = 0; i < md->numProceEsf; i++)
    {
        SPHERE* testeFim;
        if (i!=md->numProceEsf-1) testeFim = md->threads[i+1];
        else testeFim=NULL;
        for (SPHERE* s = md->threads[i]; s != testeFim; s = s->Next)
        {
            int32_t row = Max( Min( (int32_t)( (s->R[1] - MinR[1]) / tamCelula ), Rows-1 ), 0 );
            linhaProc[i][row].push_back(s);
        }
    }//Barreira implícita
    #ifdef TESTE
    #pragma omp single
    {
        assert (omp_get_num_procs()==md->numProceEsf);
        int tamanho=0;
        int tamanhoreal=0;
        for (int row=0;row<Rows;row++)
        {
            for (int i = 0; i < md->numProceEsf; i++)
            {
                tamanho+=linhaProc[i][row].size();
            }
        }
        for (SPHERE* s = md->SPHList.Begin; s != NULL; s = s->Next)
        {
            tamanhoreal++;
        }
        if (tamanho!=tamanhoreal)
        {
            cout << tamanho << ' ' << tamanhoreal << '\n';
        }
        assert(tamanho==tamanhoreal);
    }
    #endif
    //Reserva espaço
    #pragma omp for schedule (static,2)
    for (int row=0;row<Rows;row++)
    {
        int tamanho=0;
        for (int i = 0; i < md->numProceEsf; i++)
        {
            tamanho+=linhaProc[i][row].size();
        }
        linhas[row].novo=true;
        linhas[row].esferas.reserve(tamanho);
    }//Barreira implícita
    //Concatena
    #pragma omp for schedule (static,2)
    for (int row=0;row<Rows;row++)
    {
        for (int i = 0; i < md->numProceEsf; i++)
        {
            if(!linhaProc[i][row].empty())linhas[row].esferas.insert(linhas[row].esferas.end(),linhaProc[i][row].begin(),linhaProc[i][row].end());
        }
    }//Barreira implícita
    #ifdef TESTE
    #pragma omp single
    {
        int tamanho=0;
        int tamanhoreal=0;
        for (int row=0;row<Rows;row++)
        {
            tamanho+=linhas[row].esferas.size();
        }
        for (SPHERE* s = md->SPHList.Begin; s != NULL; s = s->Next)
        {
            tamanhoreal++;
        }
        assert(tamanho==tamanhoreal);
    }
    #endif

    //Limpa vetores de lista-linha-processador
    #pragma omp for schedule (static,2)
    for (int row=0;row<Rows;row++)
    {
        for (int i = 0; i < md->numProceEsf; i++)
        {
            linhaProc[i][row].clear();
        }
    }//Barreira implícita
}
void Munjiza_nbs_par::calcular()
{
    CriaVetorLinhaParalelo();
    ZeraPonteiros();
    //Etapa 2
    iteracao(0);
    iteracao(1);
    // 3  Percorra todos os elementos e remova as linhas do “vetor-linha”.
    #pragma omp single nowait
    {
        for (int i=0; i!=Rows; i++)
        {
            linhas[i].esferas.clear();
        }
    }

}

void Munjiza_nbs_par::iteracao(short int iteracao)
{
    #pragma omp for schedule (static,1)
    for (int row=iteracao;row<Rows;row+=2)
    {
        if (linhas[row].novo)
        {
            int threadAtual=omp_get_thread_num();
            //2.2  Se a posição for “nova”, esta será marcada como “observada”.
            linhas[row].novo=false;
            //2.2.1 Percorro toda a lista de elementos da linha atual
            for (int posEsfLinha=0; posEsfLinha!=linhas[row].esferas.size(); ++posEsfLinha)
            {
                SPHERE* esferaAtual = linhas[row].esferas[posEsfLinha];
            /*for (list<SPHERE*>::iterator it=linhas[row].esferas.begin(); it!=linhas[row].esferas.end(); ++it)
            {
                SPHERE* esferaAtual = *it;*/
                int32_t col = Max( Min( (int32_t)( (esferaAtual->R[0] - MinR[0]) / tamCelula ), Cols-1 ), 0 );
                esferaAtual->CellNext = ColLinhaAtual[threadAtual][col].listaEsfCel;
                ColLinhaAtual[threadAtual][col].listaEsfCel=esferaAtual;
                ColLinhaAtual[threadAtual][col].novo=true;
            }

            //2.2.2 Idem ao anterior para a linha posterior, com cuidado para não passar da última linha
            if (row+1<Rows)
            {
                for (int posEsfLinha=0; posEsfLinha!=linhas[row+1].esferas.size(); ++posEsfLinha)
                {
                    SPHERE* esferaAtual = linhas[row+1].esferas[posEsfLinha];
                /*for (list<SPHERE*>::iterator it=linhas[row+1].esferas.begin(); it!=linhas[row+1].esferas.end(); ++it)
                {
                    SPHERE* esferaAtual = *it;*/

                    int32_t col = Max( Min( (int32_t)( (esferaAtual->R[0] - MinR[0]) / tamCelula ), Cols-1 ), 0 );
                    esferaAtual->CellNext = ColLinhaPosterior[threadAtual][col].listaEsfCel;
                    ColLinhaPosterior[threadAtual][col].listaEsfCel=esferaAtual;
                    ColLinhaPosterior[threadAtual][col].novo=true;
                }
            }

            //2.2.3 Percorro toda a lista de elementos da linha atual
            for (int posEsfLinha=0; posEsfLinha!=linhas[row].esferas.size(); ++posEsfLinha)
            {
                SPHERE* esferaAtual = linhas[row].esferas[posEsfLinha];
            /*for (list<SPHERE*>::iterator it=linhas[row].esferas.begin(); it!=linhas[row].esferas.end(); ++it)
            {
                SPHERE* esferaAtual = *it;*/
                int32_t col = Max( Min( (int32_t)( (esferaAtual->R[0] - MinR[0]) / tamCelula ), Cols-1 ), 0 );

                if (ColLinhaAtual[threadAtual][col].novo)
                {
                     ColLinhaAtual[threadAtual][col].novo=false;

                     //2.2.3.1.2  Faça a detecção de contato com (x,y),(x,y-1),(x+1,y-1), (x+1,y), (x+1,y+1)
                     for (SPHERE* esferaAtual=ColLinhaAtual[threadAtual][col].listaEsfCel; esferaAtual!=NULL; esferaAtual=esferaAtual->CellNext)
                     {

                         //Comparo os elementos da própria célula (x,y)
                         for (SPHERE* esferaTmp=esferaAtual->CellNext; esferaTmp!=NULL; esferaTmp=esferaTmp->CellNext)
                         {
                             Forca->ForcaResSS(esferaAtual,esferaTmp);
                         }
                         //Testo  com os elementos da coluna anterior, se existir
                         if (col>0)
                         {
                             //Com a coluna anterior testo com os elementos da linha atual (x,y-1)
                             for (SPHERE* esferaTmp=ColLinhaAtual[threadAtual][col-1].listaEsfCel; esferaTmp!=NULL; esferaTmp=esferaTmp->CellNext)
                             {
                                 Forca->ForcaResSS(esferaAtual,esferaTmp);
                             }

                             //Com a coluna anterior testo com os elementos da linha posterior, se existirem (x+1,y-1)
                             for (SPHERE* esferaTmp=ColLinhaPosterior[threadAtual][col-1].listaEsfCel; esferaTmp!=NULL; esferaTmp=esferaTmp->CellNext)
                             {
                                 Forca->ForcaResSS(esferaAtual,esferaTmp);
                             }
                         }
                         //Com a coluna atual testo com os elementos da linha posterior, se existirem
                         // (x+1,y)
                         for (SPHERE* esferaTmp=ColLinhaPosterior[threadAtual][col].listaEsfCel; esferaTmp!=NULL; esferaTmp=esferaTmp->CellNext)
                         {
                             Forca->ForcaResSS(esferaAtual,esferaTmp);
                         }

                         // (x+1,y+1)
                         if (col+1<Cols)
                         {
                             for (SPHERE* esferaTmp=ColLinhaPosterior[threadAtual][col+1].listaEsfCel; esferaTmp!=NULL; esferaTmp=esferaTmp->CellNext)
                             {
                                 Forca->ForcaResSS(esferaAtual,esferaTmp);
                             }
                         }
                     }
                }

            }
            //  2.2.4  .Percorra todos os elementos da linha atual e remova os elementos do vetor-coluna.
            //  2.2.5  .Percorra todos os elementos da linha posterior e remova os elementos do vetor-coluna.
            for (int i=0; i!=Cols; i++)
            {
                ColLinhaAtual[threadAtual][i].listaEsfCel=NULL;
                ColLinhaPosterior[threadAtual][i].listaEsfCel=NULL;
            }
        }
    }
}
